Skip to content

Conversation

@saurabh-et-al
Copy link
Contributor

Amazon Bedrock AgentCore Samples Pull Request

Important

  1. We strictly follow a issue-first approach, please first open an issue relating to this Pull Request.
  2. Once this Pull Request is ready for review please attach review ready label to it. Only PRs with review ready will be reviewed.

Issue number:

Concise description of the PR

This pull request provides an pattern for passing bearer token from MCP client to AgentCore Gateway to enable the tools to use this bearer token (separate from the outbound auth), because customers are looking for ways to pass auth token for downstream tool integrations such as Asana, JIRA.

Renaming "04-bearer-token-injection" to "07-bearer-token-injection" under "01-tutorials/02-AgentCore-gateway/"

User experience

Users will see this new module "07-bearer-token-injection" under "01-tutorials/02-AgentCore-gateway/"

Checklist

If your change doesn't seem to apply, please leave them unchecked.

  • [x ] I have reviewed the contributing guidelines
  • [x ] Add your name to CONTRIBUTORS.md
  • [ x] Have you checked to ensure there aren't other open Pull Requests for the same update/change?
  • Are you uploading a dataset?
  • [x ] Have you documented Introduction, Architecture Diagram, Prerequisites, Usage, Sample Prompts, and Clean Up steps in your example README?
  • [ x] I agree to resolve any issues created for this example in the future.
  • [ x] I have performed a self-review of this change
  • [x ] Changes have been tested
  • [ x] Changes are documented

Acknowledgment

By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of the project license.

@review-notebook-app
Copy link

Check out this pull request on  ReviewNB

See visual diffs & provide feedback on Jupyter Notebooks.


Powered by ReviewNB

@github-actions github-actions bot added 01-tutorials 01-tutorials 02-AgentCore-gateway 01-tutorials/02-AgentCore-gateway labels Oct 22, 2025
@github-actions
Copy link

Latest scan for commit: 0547911 | Updated: 2025-10-22 17:36:03 UTC

Security Scan Results

Scan Metadata

  • Project: ASH
  • Scan executed: 2025-10-22T17:35:46+00:00
  • ASH version: 3.0.0

Summary

Scanner Results

The table below shows findings by scanner, with status based on severity thresholds and dependencies:

Column Explanations:

Severity Levels (S/C/H/M/L/I):

  • Suppressed (S): Security findings that have been explicitly suppressed/ignored and don't affect the scanner's pass/fail status
  • Critical (C): The most severe security vulnerabilities requiring immediate remediation (e.g., SQL injection, remote code execution)
  • High (H): Serious security vulnerabilities that should be addressed promptly (e.g., authentication bypasses, privilege escalation)
  • Medium (M): Moderate security risks that should be addressed in normal development cycles (e.g., weak encryption, input validation issues)
  • Low (L): Minor security concerns with limited impact (e.g., information disclosure, weak recommendations)
  • Info (I): Informational findings for awareness with minimal security risk (e.g., code quality suggestions, best practice recommendations)

Other Columns:

  • Time: Duration taken by each scanner to complete its analysis
  • Action: Total number of actionable findings at or above the configured severity threshold that require attention

Scanner Results:

  • PASSED: Scanner found no security issues at or above the configured severity threshold - code is clean for this scanner
  • FAILED: Scanner found security vulnerabilities at or above the threshold that require attention and remediation
  • MISSING: Scanner could not run because required dependencies/tools are not installed or available
  • SKIPPED: Scanner was intentionally disabled or excluded from this scan
  • ERROR: Scanner encountered an execution error and could not complete successfully

Severity Thresholds (Thresh Column):

  • CRITICAL: Only Critical severity findings cause scanner to fail
  • HIGH: High and Critical severity findings cause scanner to fail
  • MEDIUM (MED): Medium, High, and Critical severity findings cause scanner to fail
  • LOW: Low, Medium, High, and Critical severity findings cause scanner to fail
  • ALL: Any finding of any severity level causes scanner to fail

Threshold Source: Values in parentheses indicate where the threshold is configured:

  • (g) = global: Set in the global_settings section of ASH configuration
  • (c) = config: Set in the individual scanner configuration section
  • (s) = scanner: Default threshold built into the scanner itself

Statistics calculation:

  • All statistics are calculated from the final aggregated SARIF report
  • Suppressed findings are counted separately and do not contribute to actionable findings
  • Scanner status is determined by comparing actionable findings to the threshold
Scanner S C H M L I Time Action Result Thresh
bandit 0 0 0 0 0 0 681ms 0 PASSED MED (g)
cdk-nag 0 26 0 1 0 8 29.0s 27 FAILED MED (g)
cfn-nag 0 1 0 6 0 0 1.4s 7 FAILED MED (g)
checkov 0 9 0 0 0 0 5.9s 9 FAILED MED (g)
detect-secrets 0 7 0 0 0 0 859ms 7 FAILED MED (g)
grype 0 0 0 0 0 0 29.1s 0 PASSED MED (g)
npm-audit 0 0 0 0 0 0 179ms 0 PASSED MED (g)
opengrep 0 0 0 0 0 0 <1ms 0 SKIPPED MED (g)
semgrep 0 0 0 0 0 0 14.6s 0 PASSED MED (g)
syft 0 0 0 0 0 0 1.9s 0 PASSED MED (g)

Detailed Findings

Show 50 actionable findings

Finding 1: CFN_NAG_F78

  • Severity: HIGH
  • Scanner: cfn-nag
  • Rule ID: CFN_NAG_F78
  • Location: 01-tutorials/02-AgentCore-gateway/07-bearer-token-injection/prerequisites/agentcore-components/cognito.yaml:31

Description:
AWS Cognito UserPool should have MfaConfiguration set to 'ON' (MUST be wrapped in quotes) or at least 'OPTIONAL'


Finding 2: CFN_NAG_W89

  • Severity: MEDIUM
  • Scanner: cfn-nag
  • Rule ID: CFN_NAG_W89
  • Location: 01-tutorials/02-AgentCore-gateway/07-bearer-token-injection/prerequisites/agentcore-components/cognito.yaml:228

Description:
Lambda functions should be deployed inside a VPC


Finding 3: CFN_NAG_W59

  • Severity: MEDIUM
  • Scanner: cfn-nag
  • Rule ID: CFN_NAG_W59
  • Location: 01-tutorials/02-AgentCore-gateway/07-bearer-token-injection/prerequisites/agentcore-components/infrastructure_all.yaml:262

Description:
AWS::ApiGateway::Method should not have AuthorizationType set to 'NONE' unless it is of HttpMethod: OPTIONS.


Finding 4: CFN_NAG_W59

  • Severity: MEDIUM
  • Scanner: cfn-nag
  • Rule ID: CFN_NAG_W59
  • Location: 01-tutorials/02-AgentCore-gateway/07-bearer-token-injection/prerequisites/agentcore-components/infrastructure_all.yaml:276

Description:
AWS::ApiGateway::Method should not have AuthorizationType set to 'NONE' unless it is of HttpMethod: OPTIONS.


Finding 5: CFN_NAG_W89

  • Severity: MEDIUM
  • Scanner: cfn-nag
  • Rule ID: CFN_NAG_W89
  • Location: 01-tutorials/02-AgentCore-gateway/07-bearer-token-injection/prerequisites/agentcore-components/infrastructure_all.yaml:62

Description:
Lambda functions should be deployed inside a VPC


Finding 6: CFN_NAG_W84

  • Severity: MEDIUM
  • Scanner: cfn-nag
  • Rule ID: CFN_NAG_W84
  • Location: 01-tutorials/02-AgentCore-gateway/07-bearer-token-injection/prerequisites/agentcore-components/infrastructure_all.yaml:309

Description:
CloudWatchLogs LogGroup should specify a KMS Key Id to encrypt the log data


Finding 7: CFN_NAG_W28

  • Severity: MEDIUM
  • Scanner: cfn-nag
  • Rule ID: CFN_NAG_W28
  • Location: 01-tutorials/02-AgentCore-gateway/07-bearer-token-injection/prerequisites/agentcore-components/infrastructure_all.yaml:339

Description:
Resource found with an explicit name, this disallows updates that require replacement of this resource


Finding 8: SECRET-SECRET-KEYWORD

  • Severity: HIGH
  • Scanner: detect-secrets
  • Rule ID: SECRET-SECRET-KEYWORD
  • Location: 01-tutorials/02-AgentCore-gateway/07-bearer-token-injection/prerequisites/agentcore-components/cognito.yaml:119

Description:
Secret of type 'Secret Keyword' detected in file '01-tutorials/02-AgentCore-gateway/07-bearer-token-injection/prerequisites/agentcore-components/cognito.yaml' at line 119

Code Snippet:

Secret of type Secret Keyword detected

Finding 9: SECRET-SECRET-KEYWORD

  • Severity: HIGH
  • Scanner: detect-secrets
  • Rule ID: SECRET-SECRET-KEYWORD
  • Location: 01-tutorials/02-AgentCore-gateway/07-bearer-token-injection/prerequisites/agentcore-components/cognito.yaml:81

Description:
Secret of type 'Secret Keyword' detected in file '01-tutorials/02-AgentCore-gateway/07-bearer-token-injection/prerequisites/agentcore-components/cognito.yaml' at line 81

Code Snippet:

Secret of type Secret Keyword detected

Finding 10: SECRET-SECRET-KEYWORD

  • Severity: HIGH
  • Scanner: detect-secrets
  • Rule ID: SECRET-SECRET-KEYWORD
  • Location: 01-tutorials/02-AgentCore-gateway/07-bearer-token-injection/prerequisites/agentcore-components/infrastructure_all.yaml:444

Description:
Secret of type 'Secret Keyword' detected in file '01-tutorials/02-AgentCore-gateway/07-bearer-token-injection/prerequisites/agentcore-components/infrastructure_all.yaml' at line 444

Code Snippet:

Secret of type Secret Keyword detected

Finding 11: SECRET-SECRET-KEYWORD

  • Severity: HIGH
  • Scanner: detect-secrets
  • Rule ID: SECRET-SECRET-KEYWORD
  • Location: 01-tutorials/02-AgentCore-gateway/07-bearer-token-injection/prerequisites/agentcore-components/infrastructure_all.yaml:268

Description:
Secret of type 'Secret Keyword' detected in file '01-tutorials/02-AgentCore-gateway/07-bearer-token-injection/prerequisites/agentcore-components/infrastructure_all.yaml' at line 268

Code Snippet:

Secret of type Secret Keyword detected

Finding 12: SECRET-SECRET-KEYWORD

  • Severity: HIGH
  • Scanner: detect-secrets
  • Rule ID: SECRET-SECRET-KEYWORD
  • Location: 01-tutorials/02-AgentCore-gateway/07-bearer-token-injection/prerequisites/agentcore-components/infrastructure_all.yaml:443

Description:
Secret of type 'Secret Keyword' detected in file '01-tutorials/02-AgentCore-gateway/07-bearer-token-injection/prerequisites/agentcore-components/infrastructure_all.yaml' at line 443

Code Snippet:

Secret of type Secret Keyword detected

Finding 13: SECRET-SECRET-KEYWORD

  • Severity: HIGH
  • Scanner: detect-secrets
  • Rule ID: SECRET-SECRET-KEYWORD
  • Location: 01-tutorials/02-AgentCore-gateway/07-bearer-token-injection/prerequisites/agentcore-components/infrastructure_all.yaml:442

Description:
Secret of type 'Secret Keyword' detected in file '01-tutorials/02-AgentCore-gateway/07-bearer-token-injection/prerequisites/agentcore-components/infrastructure_all.yaml' at line 442

Code Snippet:

Secret of type Secret Keyword detected

Finding 14: SECRET-SECRET-KEYWORD

  • Severity: HIGH
  • Scanner: detect-secrets
  • Rule ID: SECRET-SECRET-KEYWORD
  • Location: 01-tutorials/02-AgentCore-gateway/07-bearer-token-injection/prerequisites/agentcore-components/infrastructure_all.yaml:440

Description:
Secret of type 'Secret Keyword' detected in file '01-tutorials/02-AgentCore-gateway/07-bearer-token-injection/prerequisites/agentcore-components/infrastructure_all.yaml' at line 440

Code Snippet:

Secret of type Secret Keyword detected

Finding 15: CKV_AWS_117

  • Severity: HIGH
  • Scanner: checkov
  • Rule ID: CKV_AWS_117
  • Location: 01-tutorials/02-AgentCore-gateway/07-bearer-token-injection/prerequisites/agentcore-components/cognito.yaml:227-282

Description:
Ensure that AWS Lambda function is configured inside a VPC

Code Snippet:

PostSignupFunction:
    Type: AWS::Lambda::Function
    Properties:
      FunctionName: PostSignupFunction
      Handler: index.lambda_handler
      Runtime: python3.13
      Role: !GetAtt PostSignupFunctionRole.Arn
      Timeout: 10
      MemorySize: 128
      ReservedConcurrentExecutions: 5 # Limit concurrent executions
      DeadLetterConfig:
        TargetArn: !GetAtt PostSignupFunctionDLQ.Arn
      Environment:
        Variables:
          LOG_LEVEL: INFO
      Code:
        ZipFile: |
          import boto3
          import json
          import logging
          import os

          # Configure logging
          logger = logging.getLogger()
          logger.setLevel(os.environ.get('LOG_LEVEL', 'INFO'))

          def lambda_handler(event, context):
              try:
                  # Validate event structure
                  if not event or 'userPoolId' not in event or 'userName' not in event:
                      logger.error("Invalid event structure")
                      raise ValueError("Missing required event fields")
                  
                  user_pool_id = event['userPoolId']
                  username = event['userName']
                  
                  # Validate inputs
                  if not user_pool_id or not username:
                      logger.error("Empty userPoolId or userName")
                      raise ValueError("userPoolId and userName cannot be empty")
                  
                  client = boto3.client('cognito-idp')

                  # Add user to 'Customer' group
                  client.admin_add_user_to_group(
                      UserPoolId=user_pool_id,
                      Username=username,
                      GroupName='Customer'
                  )
                  logger.info(f"User {username} added to 'Customer' group successfully")
                  
                  return event
                  
              except Exception as e:
                  logger.error(f"Error processing signup: {str(e)}")
                  raise

Finding 16: CKV_AWS_173

  • Severity: HIGH
  • Scanner: checkov
  • Rule ID: CKV_AWS_173
  • Location: 01-tutorials/02-AgentCore-gateway/07-bearer-token-injection/prerequisites/agentcore-components/cognito.yaml:227-282

Description:
Check encryption settings for Lambda environment variable

Code Snippet:

PostSignupFunction:
    Type: AWS::Lambda::Function
    Properties:
      FunctionName: PostSignupFunction
      Handler: index.lambda_handler
      Runtime: python3.13
      Role: !GetAtt PostSignupFunctionRole.Arn
      Timeout: 10
      MemorySize: 128
      ReservedConcurrentExecutions: 5 # Limit concurrent executions
      DeadLetterConfig:
        TargetArn: !GetAtt PostSignupFunctionDLQ.Arn
      Environment:
        Variables:
          LOG_LEVEL: INFO
      Code:
        ZipFile: |
          import boto3
          import json
          import logging
          import os

          # Configure logging
          logger = logging.getLogger()
          logger.setLevel(os.environ.get('LOG_LEVEL', 'INFO'))

          def lambda_handler(event, context):
              try:
                  # Validate event structure
                  if not event or 'userPoolId' not in event or 'userName' not in event:
                      logger.error("Invalid event structure")
                      raise ValueError("Missing required event fields")
                  
                  user_pool_id = event['userPoolId']
                  username = event['userName']
                  
                  # Validate inputs
                  if not user_pool_id or not username:
                      logger.error("Empty userPoolId or userName")
                      raise ValueError("userPoolId and userName cannot be empty")
                  
                  client = boto3.client('cognito-idp')

                  # Add user to 'Customer' group
                  client.admin_add_user_to_group(
                      UserPoolId=user_pool_id,
                      Username=username,
                      GroupName='Customer'
                  )
                  logger.info(f"User {username} added to 'Customer' group successfully")
                  
                  return event
                  
              except Exception as e:
                  logger.error(f"Error processing signup: {str(e)}")
                  raise

Finding 17: CKV_AWS_117

  • Severity: HIGH
  • Scanner: checkov
  • Rule ID: CKV_AWS_117
  • Location: 01-tutorials/02-AgentCore-gateway/07-bearer-token-injection/prerequisites/agentcore-components/infrastructure_all.yaml:61-238

Description:
Ensure that AWS Lambda function is configured inside a VPC

Code Snippet:

AsanaIntegrationApiLambdaFunction:
    Type: AWS::Lambda::Function
    Properties:
      FunctionName: AgentCoreGwyAsanaIntegrationDemo
      Runtime: python3.12
      Handler: index.lambda_handler
      Role: !GetAtt AsanaIntegrationLambdaRole.Arn
      Timeout: 30
      MemorySize: 128
      ReservedConcurrentExecutions: 10 # Limit concurrent executions
      DeadLetterConfig:
        TargetArn: !GetAtt AsanaIntegrationLambdaDLQ.Arn
      Environment:
        Variables:
          LOG_LEVEL: INFO
      Code:
        ZipFile: |
          import json
          import os
          import logging
          from typing import Dict, Any
          from datetime import datetime

          # Configure logging
          logger = logging.getLogger()
          logger.setLevel(logging.INFO)

          def lambda_handler(event: Dict[str, Any], context: Any) -> Dict[str, Any]:
              """
              Lambda function handler for Asana Integration Demo
              
              Args:
                  event: API Gateway event
                  context: Lambda context
                  
              Returns:
                  API Gateway response
              """
              try:
                  # Validate event structure
                  if not isinstance(event, dict):
                      logger.error("Invalid event structure received")
                      return create_response(400, {'error': 'Invalid request format'})
                  
                  # Log minimal event info (avoid logging sensitive data)
                  logger.info(f"Processing request - Method: {event.get('httpMethod', 'UNKNOWN')}, Path: {event.get('path', 'UNKNOWN')}")
                  
                  # Get HTTP method and path with validation
                  http_method = event.get('httpMethod', '').upper()
                  path = event.get('path', '')
                  
                  # Validate required fields
                  if not http_method or not path:
                      logger.error("Missing required fields in event")
                      return create_response(400, {'error': 'Invalid request structure'})
                  
                  # Route based on method and path
                  if http_method == 'GET' and path == '/asana':
                      return handle_get_request(event)
                  elif http_method == 'POST' and path == '/asana':
                      return handle_post_request(event)
                  else:
                      logger.warning(f"Unsupported endpoint: {http_method} {path}")
                      return create_response(404, {'error': 'Not Found'})
                      
              except Exception as e:
                  logger.error(f"Error processing request: {str(e)}")
                  return create_response(500, {'error': 'Internal Server Error'})

          def handle_get_request(event: Dict[str, Any]) -> Dict[str, Any]:
              """Handle GET requests to /asana endpoint"""
              
              # Get query parameters
              query_params = event.get('queryStringParameters') or {}
              
              response_data = {
                  'message': 'AgentCore Asana Integration Demo',
                  'status': 'active',
                  'method': 'GET',
                  'endpoint': '/asana',
                  'query_params': query_params
              }
              
              return create_response(200, response_data)

          def handle_post_request(event: Dict[str, Any]) -> Dict[str, Any]:
              """Handle POST requests to /asana endpoint"""
              
              try:
                  # Parse request body with size validation
                  headers = event.get('headers', '{}')
                  logger.info(f"Headers: {headers}")

                  body = event.get('body', '{}')
                  logger.info(f"POST data: {body}")
                  
                  # Validate body size (prevent large payloads)
                  if isinstance(body, str) and len(body) > 1024 * 1024:  # 1MB limit
                      logger.error("Request body too large")
                      return create_response(413, {'error': 'Request body too large'})
                  
                  if isinstance(body, str):
                      if not body.strip():
                          post_data = {}
                      else:
                          post_data = json.loads(body)
                  else:
                      post_data = body if body else {}
                  
                  # Validate post_data structure
                  if not isinstance(post_data, dict):
                      logger.error("POST data must be a JSON object")
                      return create_response(400, {'error': 'Request body must be a JSON object'})
                  
                  # Validate required fields
                  tool_name = post_data.get('tool_name')
                  if not tool_name or not isinstance(tool_name, str):
                      logger.error("Missing or invalid tool_name")
                      return create_response(400, {'error': 'tool_name is required and must be a string'})
                  
                  # Sanitize tool_name (basic validation)
                  if not tool_name.replace('_', '').replace('-', '').isalnum():
                      logger.error("Invalid tool_name format")
                      return create_response(400, {'error': 'tool_name contains invalid characters'})
                  
                  # Log minimal info (avoid logging sensitive data)
                  logger.info(f"Processing tool: {tool_name}")
                  
                  # Validate headers for bearer token
                  headers = event.get('headers', {})
                  asana_token = headers.get('X-Asana-Token') or headers.get('x-asana-token')
                  
                  if not asana_token:
                      logger.error("Missing X-Asana-Token header")
                      return create_response(401, {'error': 'X-Asana-Token header is required'})
                  
                  current_datetime = datetime.now()
                  current_time = current_datetime.strftime("%Y-%m-%d %H:%M:%S")

                  # Process POST data (placeholder for actual integration logic)
                  response_data = {
                      'message': 'POST request processed successfully',
                      'method': 'POST',
                      'endpoint': '/asana',
                      'processed': True,
                      'tool_name': tool_name,
                      'timestamp': current_time
                  }
                  
                  return create_response(200, response_data)
                  
              except json.JSONDecodeError as e:
                  logger.error(f"Invalid JSON in POST body: {str(e)}")
                  return create_response(400, {'error': 'Invalid JSON format'})
              except Exception as e:
                  logger.error(f"Error processing POST request: {str(e)}")
                  return create_response(500, {'error': 'Internal server error'})

          def create_response(status_code: int, body: Dict[str, Any]) -> Dict[str, Any]:
              """Create standardized API Gateway response with security headers"""
              
              return {
                  'statusCode': status_code,
                  'headers': {
                      'Content-Type': 'application/json',
                      'Access-Control-Allow-Origin': '*',  # Consider restricting in production
                      'Access-Control-Allow-Headers': 'Content-Type,X-Asana-Token,Authorization',
                      'Access-Control-Allow-Methods': 'GET,POST,OPTIONS',
                      'X-Content-Type-Options': 'nosniff',
                      'X-Frame-Options': 'DENY',
                      'X-XSS-Protection': '1; mode=block',
                      'Strict-Transport-Security': 'max-age=31536000; includeSubDomains',
                      'Cache-Control': 'no-cache, no-store, must-revalidate',
                      'Pragma': 'no-cache',
                      'Expires': '0'
                  },
                  'body': json.dumps(body, separators=(',', ':'))  # Compact JSON
              }

Finding 18: CKV_AWS_173

  • Severity: HIGH
  • Scanner: checkov
  • Rule ID: CKV_AWS_173
  • Location: 01-tutorials/02-AgentCore-gateway/07-bearer-token-injection/prerequisites/agentcore-components/infrastructure_all.yaml:61-238

Description:
Check encryption settings for Lambda environment variable

Code Snippet:

AsanaIntegrationApiLambdaFunction:
    Type: AWS::Lambda::Function
    Properties:
      FunctionName: AgentCoreGwyAsanaIntegrationDemo
      Runtime: python3.12
      Handler: index.lambda_handler
      Role: !GetAtt AsanaIntegrationLambdaRole.Arn
      Timeout: 30
      MemorySize: 128
      ReservedConcurrentExecutions: 10 # Limit concurrent executions
      DeadLetterConfig:
        TargetArn: !GetAtt AsanaIntegrationLambdaDLQ.Arn
      Environment:
        Variables:
          LOG_LEVEL: INFO
      Code:
        ZipFile: |
          import json
          import os
          import logging
          from typing import Dict, Any
          from datetime import datetime

          # Configure logging
          logger = logging.getLogger()
          logger.setLevel(logging.INFO)

          def lambda_handler(event: Dict[str, Any], context: Any) -> Dict[str, Any]:
              """
              Lambda function handler for Asana Integration Demo
              
              Args:
                  event: API Gateway event
                  context: Lambda context
                  
              Returns:
                  API Gateway response
              """
              try:
                  # Validate event structure
                  if not isinstance(event, dict):
                      logger.error("Invalid event structure received")
                      return create_response(400, {'error': 'Invalid request format'})
                  
                  # Log minimal event info (avoid logging sensitive data)
                  logger.info(f"Processing request - Method: {event.get('httpMethod', 'UNKNOWN')}, Path: {event.get('path', 'UNKNOWN')}")
                  
                  # Get HTTP method and path with validation
                  http_method = event.get('httpMethod', '').upper()
                  path = event.get('path', '')
                  
                  # Validate required fields
                  if not http_method or not path:
                      logger.error("Missing required fields in event")
                      return create_response(400, {'error': 'Invalid request structure'})
                  
                  # Route based on method and path
                  if http_method == 'GET' and path == '/asana':
                      return handle_get_request(event)
                  elif http_method == 'POST' and path == '/asana':
                      return handle_post_request(event)
                  else:
                      logger.warning(f"Unsupported endpoint: {http_method} {path}")
                      return create_response(404, {'error': 'Not Found'})
                      
              except Exception as e:
                  logger.error(f"Error processing request: {str(e)}")
                  return create_response(500, {'error': 'Internal Server Error'})

          def handle_get_request(event: Dict[str, Any]) -> Dict[str, Any]:
              """Handle GET requests to /asana endpoint"""
              
              # Get query parameters
              query_params = event.get('queryStringParameters') or {}
              
              response_data = {
                  'message': 'AgentCore Asana Integration Demo',
                  'status': 'active',
                  'method': 'GET',
                  'endpoint': '/asana',
                  'query_params': query_params
              }
              
              return create_response(200, response_data)

          def handle_post_request(event: Dict[str, Any]) -> Dict[str, Any]:
              """Handle POST requests to /asana endpoint"""
              
              try:
                  # Parse request body with size validation
                  headers = event.get('headers', '{}')
                  logger.info(f"Headers: {headers}")

                  body = event.get('body', '{}')
                  logger.info(f"POST data: {body}")
                  
                  # Validate body size (prevent large payloads)
                  if isinstance(body, str) and len(body) > 1024 * 1024:  # 1MB limit
                      logger.error("Request body too large")
                      return create_response(413, {'error': 'Request body too large'})
                  
                  if isinstance(body, str):
                      if not body.strip():
                          post_data = {}
                      else:
                          post_data = json.loads(body)
                  else:
                      post_data = body if body else {}
                  
                  # Validate post_data structure
                  if not isinstance(post_data, dict):
                      logger.error("POST data must be a JSON object")
                      return create_response(400, {'error': 'Request body must be a JSON object'})
                  
                  # Validate required fields
                  tool_name = post_data.get('tool_name')
                  if not tool_name or not isinstance(tool_name, str):
                      logger.error("Missing or invalid tool_name")
                      return create_response(400, {'error': 'tool_name is required and must be a string'})
                  
                  # Sanitize tool_name (basic validation)
                  if not tool_name.replace('_', '').replace('-', '').isalnum():
                      logger.error("Invalid tool_name format")
                      return create_response(400, {'error': 'tool_name contains invalid characters'})
                  
                  # Log minimal info (avoid logging sensitive data)
                  logger.info(f"Processing tool: {tool_name}")
                  
                  # Validate headers for bearer token
                  headers = event.get('headers', {})
                  asana_token = headers.get('X-Asana-Token') or headers.get('x-asana-token')
                  
                  if not asana_token:
                      logger.error("Missing X-Asana-Token header")
                      return create_response(401, {'error': 'X-Asana-Token header is required'})
                  
                  current_datetime = datetime.now()
                  current_time = current_datetime.strftime("%Y-%m-%d %H:%M:%S")

                  # Process POST data (placeholder for actual integration logic)
                  response_data = {
                      'message': 'POST request processed successfully',
                      'method': 'POST',
                      'endpoint': '/asana',
                      'processed': True,
                      'tool_name': tool_name,
                      'timestamp': current_time
                  }
                  
                  return create_response(200, response_data)
                  
              except json.JSONDecodeError as e:
                  logger.error(f"Invalid JSON in POST body: {str(e)}")
                  return create_response(400, {'error': 'Invalid JSON format'})
              except Exception as e:
                  logger.error(f"Error processing POST request: {str(e)}")
                  return create_response(500, {'error': 'Internal server error'})

          def create_response(status_code: int, body: Dict[str, Any]) -> Dict[str, Any]:
              """Create standardized API Gateway response with security headers"""
              
              return {
                  'statusCode': status_code,
                  'headers': {
                      'Content-Type': 'application/json',
                      'Access-Control-Allow-Origin': '*',  # Consider restricting in production
                      'Access-Control-Allow-Headers': 'Content-Type,X-Asana-Token,Authorization',
                      'Access-Control-Allow-Methods': 'GET,POST,OPTIONS',
                      'X-Content-Type-Options': 'nosniff',
                      'X-Frame-Options': 'DENY',
                      'X-XSS-Protection': '1; mode=block',
                      'Strict-Transport-Security': 'max-age=31536000; includeSubDomains',
                      'Cache-Control': 'no-cache, no-store, must-revalidate',
                      'Pragma': 'no-cache',
                      'Expires': '0'
                  },
                  'body': json.dumps(body, separators=(',', ':'))  # Compact JSON
              }

Finding 19: CKV_AWS_59

  • Severity: HIGH
  • Scanner: checkov
  • Rule ID: CKV_AWS_59
  • Location: 01-tutorials/02-AgentCore-gateway/07-bearer-token-injection/prerequisites/agentcore-components/infrastructure_all.yaml:261-272

Description:
Ensure there is no open access to back-end resources through API

Code Snippet:

AsanaGetMethod:
    Type: AWS::ApiGateway::Method
    Properties:
      RestApiId: !Ref AsanaIntegrationApiGateway
      ResourceId: !Ref AsanaResource
      HttpMethod: GET
      AuthorizationType: NONE
      ApiKeyRequired: false
      Integration:
        Type: AWS_PROXY
        IntegrationHttpMethod: POST
        Uri: !Sub "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${AsanaIntegrationApiLambdaFunction.Arn}/invocations"

Finding 20: CKV_AWS_59

  • Severity: HIGH
  • Scanner: checkov
  • Rule ID: CKV_AWS_59
  • Location: 01-tutorials/02-AgentCore-gateway/07-bearer-token-injection/prerequisites/agentcore-components/infrastructure_all.yaml:275-286

Description:
Ensure there is no open access to back-end resources through API

Code Snippet:

AsanaPostMethod:
    Type: AWS::ApiGateway::Method
    Properties:
      RestApiId: !Ref AsanaIntegrationApiGateway
      ResourceId: !Ref AsanaResource
      HttpMethod: POST
      AuthorizationType: NONE
      ApiKeyRequired: false
      Integration:
        Type: AWS_PROXY
        IntegrationHttpMethod: POST
        Uri: !Sub "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${AsanaIntegrationApiLambdaFunction.Arn}/invocations"

Finding 21: CKV_AWS_158

  • Severity: HIGH
  • Scanner: checkov
  • Rule ID: CKV_AWS_158
  • Location: 01-tutorials/02-AgentCore-gateway/07-bearer-token-injection/prerequisites/agentcore-components/infrastructure_all.yaml:308-312

Description:
Ensure that CloudWatch Log Group is encrypted by KMS

Code Snippet:

AsanaApiGatewayLogGroup:
    Type: AWS::Logs::LogGroup
    Properties:
      LogGroupName: !Sub "/aws/apigateway/${AsanaIntegrationApiGateway}"
      RetentionInDays: 30

Finding 22: CKV_SECRET_6

  • Severity: HIGH
  • Scanner: checkov
  • Rule ID: CKV_SECRET_6
  • Location: 01-tutorials/02-AgentCore-gateway/07-bearer-token-injection/prerequisites/agentcore-components/infrastructure_all.yaml:191-192

Description:
Base64 High Entropy String

Code Snippet:

asana_token = headers.get('X-A**********') or headers.get('x-a**********')

Finding 23: CKV_SECRET_6

  • Severity: HIGH
  • Scanner: checkov
  • Rule ID: CKV_SECRET_6
  • Location: 01-tutorials/02-AgentCore-gateway/07-bearer-token-injection/prerequisites/agentcore-components/infrastructure_all.yaml:191-192

Description:
Base64 High Entropy String

Code Snippet:

asana_token = headers.get('X-A**********') or headers.get('x-a**********')

Finding 24: AwsSolutions-COG2

  • Severity: MEDIUM
  • Scanner: cdk-nag
  • Rule ID: AwsSolutions-COG2
  • Location: 01-tutorials/02-AgentCore-gateway/07-bearer-token-injection/prerequisites/agentcore-components/cognito.yaml:8

Description:
The Cognito user pool does not require MFA.

Exception Reason: N/A

Code Snippet:

Resources:
  UserPool:
    Properties:
      AccountRecoverySetting:
        RecoveryMechanisms:
          - Name: verified_email
            Priority: 1
      AutoVerifiedAttributes:
        - email
      MfaConfiguration: 'OFF'
      Policies:
        PasswordPolicy:
          MinimumLength: 12
          RequireLowercase: true
          RequireNumbers: true
          RequireSymbols: true
          RequireUppercase: true
          TemporaryPasswordValidityDays: 1
      UserPoolAddOns:
        AdvancedSecurityMode: ENFORCED
      UserPoolName:
        Ref: UserPoolName
      UsernameAttributes:
        - email
      UsernameConfiguration:
        CaseSensitive: false
    Type: AWS::Cognito::UserPool

Finding 25: AwsSolutions-SQS3

  • Severity: HIGH
  • Scanner: cdk-nag
  • Rule ID: AwsSolutions-SQS3
  • Location: 01-tutorials/02-AgentCore-gateway/07-bearer-token-injection/prerequisites/agentcore-components/cognito.yaml:216

Description:
The SQS queue is not used as a dead-letter queue (DLQ) and does not have a DLQ enabled.

Exception Reason: N/A

Code Snippet:

Resources:
  PostSignupFunctionDLQ:
    Properties:
      KmsMasterKeyId: alias/aws/sqs
      MessageRetentionPeriod: 1209600
      QueueName: PostSignupFunction-DLQ
    Type: AWS::SQS::Queue

Finding 26: AwsSolutions-SQS4

  • Severity: HIGH
  • Scanner: cdk-nag
  • Rule ID: AwsSolutions-SQS4
  • Location: 01-tutorials/02-AgentCore-gateway/07-bearer-token-injection/prerequisites/agentcore-components/cognito.yaml:216

Description:
The SQS queue does not require requests to use SSL.

Exception Reason: N/A

Code Snippet:

Resources:
  PostSignupFunctionDLQ:
    Properties:
      KmsMasterKeyId: alias/aws/sqs
      MessageRetentionPeriod: 1209600
      QueueName: PostSignupFunction-DLQ
    Type: AWS::SQS::Queue

Finding 27: AwsSolutions-IAM4

  • Severity: HIGH
  • Scanner: cdk-nag
  • Rule ID: AwsSolutions-IAM4
  • Location: 01-tutorials/02-AgentCore-gateway/07-bearer-token-injection/prerequisites/agentcore-components/cognito.yaml:178

Description:
The IAM user, role, or group uses AWS managed policies.

Exception Reason: N/A

Code Snippet:

Resources:
  PostSignupFunctionRole:
    Properties:
      AssumeRolePolicyDocument:
        Statement:
          - Action: sts:AssumeRole
            Effect: Allow
            Principal:
              Service: lambda.amazonaws.com
        Version: '2012-10-17'
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole
      Policies:
        - PolicyDocument:
            Statement:
              - Action:
                  - logs:CreateLogGroup
                  - logs:CreateLogStream
                  - logs:PutLogEvents
                Effect: Allow
                Resource:
                  Fn::Sub: arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/PostSignupFunction*
            Version: '2012-10-17'
          PolicyName: AllowBasicLogs
        - PolicyDocument:
            Statement:
              - Action:
                  - cognito-idp:AdminAddUserToGroup
                Effect: Allow
                Resource:
                  Fn::GetAtt:
                    - UserPool
                    - Arn
            Version: '2012-10-17'
          PolicyName: CognitoUserPoolAccessPolicy
        - PolicyDocument:
            Statement:
              - Action:
                  - sqs:SendMessage
                Effect: Allow
                Resource:


<!-- ASH-SECURITY-SCAN-COMMENT -->

@dhawalkp dhawalkp merged commit 74d2654 into awslabs:main Oct 22, 2025
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

01-tutorials 01-tutorials 02-AgentCore-gateway 01-tutorials/02-AgentCore-gateway

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants